home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Resources / Developers / XAMPP 1.5.4 / Windows installer / xampp-win32-1.5.4-installer.exe / xampp / php / pear / PEAR / REST.php < prev    next >
Encoding:
PHP Script  |  2005-12-02  |  14.3 KB  |  380 lines

  1. <?php
  2. /**
  3.  * PEAR_REST
  4.  *
  5.  * PHP versions 4 and 5
  6.  *
  7.  * LICENSE: This source file is subject to version 3.0 of the PHP license
  8.  * that is available through the world-wide-web at the following URI:
  9.  * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
  10.  * the PHP License and are unable to obtain it through the web, please
  11.  * send a note to license@php.net so we can mail you a copy immediately.
  12.  *
  13.  * @category   pear
  14.  * @package    PEAR
  15.  * @author     Greg Beaver <cellog@php.net>
  16.  * @copyright  1997-2005 The PHP Group
  17.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  18.  * @version    CVS: $Id: REST.php,v 1.15 2005/09/24 04:15:13 cellog Exp $
  19.  * @link       http://pear.php.net/package/PEAR
  20.  * @since      File available since Release 1.4.0a1
  21.  */
  22.  
  23. /**
  24.  * For downloading xml files
  25.  */
  26. require_once 'PEAR.php';
  27. require_once 'PEAR/XMLParser.php';
  28.  
  29. /**
  30.  * Intelligently retrieve data, following hyperlinks if necessary, and re-directing
  31.  * as well
  32.  * @category   pear
  33.  * @package    PEAR
  34.  * @author     Greg Beaver <cellog@php.net>
  35.  * @copyright  1997-2005 The PHP Group
  36.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  37.  * @version    Release: 1.4.5
  38.  * @link       http://pear.php.net/package/PEAR
  39.  * @since      Class available since Release 1.4.0a1
  40.  */
  41. class PEAR_REST
  42. {
  43.     var $config;
  44.     var $_options;
  45.     function PEAR_REST(&$config, $options = array())
  46.     {
  47.         $this->config = &$config;
  48.         $this->_options = $options;
  49.     }
  50.  
  51.     /**
  52.      * Retrieve REST data, but always retrieve the local cache if it is available.
  53.      *
  54.      * This is useful for elements that should never change, such as information on a particular
  55.      * release
  56.      * @param string full URL to this resource
  57.      * @param array|false contents of the accept-encoding header
  58.      * @param boolean     if true, xml will be returned as a string, otherwise, xml will be
  59.      *                    parsed using PEAR_XMLParser
  60.      * @return string|array
  61.      */
  62.     function retrieveCacheFirst($url, $accept = false, $forcestring = false)
  63.     {
  64.         $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
  65.             md5($url) . 'rest.cachefile';
  66.         if (@file_exists($cachefile)) {
  67.             return unserialize(implode('', file($cachefile)));
  68.         }
  69.         return $this->retrieveData($url, $accept, $forcestring);
  70.     }
  71.  
  72.     /**
  73.      * Retrieve a remote REST resource
  74.      * @param string full URL to this resource
  75.      * @param array|false contents of the accept-encoding header
  76.      * @param boolean     if true, xml will be returned as a string, otherwise, xml will be
  77.      *                    parsed using PEAR_XMLParser
  78.      * @return string|array
  79.      */
  80.     function retrieveData($url, $accept = false, $forcestring = false)
  81.     {
  82.         $cacheId = $this->getCacheId($url);
  83.         if ($ret = $this->useLocalCache($url, $cacheId)) {
  84.             return $ret;
  85.         }
  86.         if (!isset($this->_options['offline'])) {
  87.             $trieddownload = true;
  88.             $file = $this->downloadHttp($url, $cacheId ? $cacheId['lastChange'] : false, $accept);
  89.         } else {
  90.             $trieddownload = false;
  91.             $file = false;
  92.         }
  93.         if (PEAR::isError($file)) {
  94.             if ($file->getCode() == -9276) {
  95.                 $trieddownload = false;
  96.                 $file = false; // use local copy if available on socket connect error
  97.             } else {
  98.                 return $file;
  99.             }
  100.         }
  101.         if (!$file) {
  102.             $ret = $this->getCache($url);
  103.             if (!PEAR::isError($ret) && $trieddownload) {
  104.                 // reset the age of the cache if the server says it was unmodified
  105.                 $this->saveCache($url, $ret, null, true, $cacheId);
  106.             }
  107.             return $ret;
  108.         }
  109.         $headers = $file[2];
  110.         $lastmodified = $file[1];
  111.         $content = $file[0];
  112.         if ($forcestring) {
  113.             $this->saveCache($url, $content, $lastmodified, false, $cacheId);
  114.             return $content;
  115.         }
  116.         if (isset($headers['content-type'])) {
  117.             switch ($headers['content-type']) {
  118.                 case 'text/xml' :
  119.                 case 'application/xml' :
  120.                     $parser = new PEAR_XMLParser;
  121.                     PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  122.                     $err = $parser->parse($content);
  123.                     PEAR::popErrorHandling();
  124.                     if (PEAR::isError($err)) {
  125.                         return PEAR::raiseError('Invalid xml downloaded from "' . $url . '": ' .
  126.                             $err->getMessage());
  127.                     }
  128.                     $content = $parser->getData();
  129.                 case 'text/html' :
  130.                 default :
  131.                     // use it as a string
  132.             }
  133.         } else {
  134.             // assume XML
  135.             $parser = new PEAR_XMLParser;
  136.             $parser->parse($file);
  137.             $content = $parser->getData();
  138.         }
  139.         $this->saveCache($url, $content, $lastmodified, false, $cacheId);
  140.         return $content;
  141.     }
  142.  
  143.     function useLocalCache($url, $cacheid = null)
  144.     {
  145.         if ($cacheid === null) {
  146.             $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
  147.                 md5($url) . 'rest.cacheid';
  148.             if (@file_exists($cacheidfile)) {
  149.                 $cacheid = unserialize(implode('', file($cacheidfile)));
  150.             } else {
  151.                 return false;
  152.             }
  153.         }
  154.         $cachettl = $this->config->get('cache_ttl');
  155.         // If cache is newer than $cachettl seconds, we use the cache!
  156.         if (time() - $cacheid['age'] < $cachettl) {
  157.             return $this->getCache($url);
  158.         }
  159.         return false;
  160.     }
  161.  
  162.     function getCacheId($url)
  163.     {
  164.         $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
  165.             md5($url) . 'rest.cacheid';
  166.         if (@file_exists($cacheidfile)) {
  167.             $ret = unserialize(implode('', file($cacheidfile)));
  168.             return $ret;
  169.         } else {
  170.             return false;
  171.         }
  172.     }
  173.  
  174.     function getCache($url)
  175.     {
  176.         $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
  177.             md5($url) . 'rest.cachefile';
  178.         if (@file_exists($cachefile)) {
  179.             return unserialize(implode('', file($cachefile)));
  180.         } else {
  181.             return PEAR::raiseError('No cached content available for "' . $url . '"');
  182.         }
  183.     }
  184.  
  185.     /**
  186.      * @param string full URL to REST resource
  187.      * @param string original contents of the REST resource
  188.      * @param array  HTTP Last-Modified and ETag headers
  189.      * @param bool   if true, then the cache id file should be regenerated to
  190.      *               trigger a new time-to-live value
  191.      */
  192.     function saveCache($url, $contents, $lastmodified, $nochange = false, $cacheid = null)
  193.     {
  194.         $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
  195.             md5($url) . 'rest.cacheid';
  196.         $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
  197.             md5($url) . 'rest.cachefile';
  198.         if ($cacheid === null && $nochange) {
  199.             $cacheid = unserialize(implode('', file($cacheidfile)));
  200.         }
  201.         $fp = @fopen($cacheidfile, 'wb');
  202.         if (!$fp) {
  203.             return false;
  204.         }
  205.         if ($nochange) {
  206.             fwrite($fp, serialize(array(
  207.                 'age'        => time(),
  208.                 'lastChange' => $cacheid['lastChange'],
  209.                 )));
  210.             fclose($fp);
  211.             return true;
  212.         } else {
  213.             fwrite($fp, serialize(array(
  214.                 'age'        => time(),
  215.                 'lastChange' => $lastmodified,
  216.                 )));
  217.         }
  218.         fclose($fp);
  219.         $fp = @fopen($cachefile, 'wb');
  220.         if (!$fp) {
  221.             @unlink($cacheidfile);
  222.             return false;
  223.         }
  224.         fwrite($fp, serialize($contents));
  225.         fclose($fp);
  226.         return true;
  227.     }
  228.  
  229.     /**
  230.      * Efficiently Download a file through HTTP.  Returns downloaded file as a string in-memory
  231.      * This is best used for small files
  232.      *
  233.      * If an HTTP proxy has been configured (http_proxy PEAR_Config
  234.      * setting), the proxy will be used.
  235.      *
  236.      * @param string  $url       the URL to download
  237.      * @param string  $save_dir  directory to save file in
  238.      * @param false|string|array $lastmodified header values to check against for caching
  239.      *                           use false to return the header values from this download
  240.      * @param false|array $accept Accept headers to send
  241.      * @return string|array  Returns the contents of the downloaded file or a PEAR
  242.      *                       error on failure.  If the error is caused by
  243.      *                       socket-related errors, the error object will
  244.      *                       have the fsockopen error code available through
  245.      *                       getCode().  If caching is requested, then return the header
  246.      *                       values.
  247.      *
  248.      * @access public
  249.      */
  250.     function downloadHttp($url, $lastmodified = null, $accept = false)
  251.     {
  252.         $info = parse_url($url);
  253.         if (!isset($info['scheme']) || !in_array($info['scheme'], array('http', 'https'))) {
  254.             return PEAR::raiseError('Cannot download non-http URL "' . $url . '"');
  255.         }
  256.         if (!isset($info['host'])) {
  257.             return PEAR::raiseError('Cannot download from non-URL "' . $url . '"');
  258.         } else {
  259.             $host = $info['host'];
  260.             if (!array_key_exists('port', $info)) {
  261.                 $info['port'] = null;
  262.             }
  263.             if (!array_key_exists('path', $info)) {
  264.                 $info['path'] = null;
  265.             }
  266.             $port = $info['port'];
  267.             $path = $info['path'];
  268.         }
  269.         $proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
  270.         if ($this->config->get('http_proxy')&& 
  271.               $proxy = parse_url($this->config->get('http_proxy'))) {
  272.             $proxy_host = @$proxy['host'];
  273.             if (isset($proxy['scheme']) && $proxy['scheme'] == 'https') {
  274.                 $proxy_host = 'ssl://' . $proxy_host;
  275.             }
  276.             $proxy_port = @$proxy['port'];
  277.             $proxy_user = @$proxy['user'];
  278.             $proxy_pass = @$proxy['pass'];
  279.  
  280.             if ($proxy_port == '') {
  281.                 $proxy_port = 8080;
  282.             }
  283.         }
  284.         if (empty($port)) {
  285.             if (isset($info['scheme']) && $info['scheme'] == 'https') {
  286.                 $port = 443;
  287.             } else {
  288.                 $port = 80;
  289.             }
  290.         }
  291.         If (isset($proxy['host'])) {
  292.             $request = "GET $url HTTP/1.1\r\n";
  293.         } else {
  294.             $request = "GET $path HTTP/1.1\r\n";
  295.         }
  296.  
  297.         $ifmodifiedsince = '';
  298.         if (is_array($lastmodified)) {
  299.             if (isset($lastmodified['Last-Modified'])) {
  300.                 $ifmodifiedsince = 'If-Modified-Since: ' . $lastmodified['Last-Modified'] . "\r\n";
  301.             }
  302.             if (isset($lastmodified['ETag'])) {
  303.                 $ifmodifiedsince .= "If-None-Match: $lastmodified[ETag]\r\n";
  304.             }
  305.         } else {
  306.             $ifmodifiedsince = ($lastmodified ? "If-Modified-Since: $lastmodified\r\n" : '');
  307.         }
  308.         $request .= "Host: $host:$port\r\n" . $ifmodifiedsince .
  309.             "User-Agent: PEAR/1.4.5/PHP/" . PHP_VERSION . "\r\n";
  310.         $username = $this->config->get('username');
  311.         $password = $this->config->get('password');
  312.         if ($username && $password) {
  313.             $tmp = base64_encode("$username:$password");
  314.             $request .= "Authorization: Basic $tmp\r\n";
  315.         }
  316.         if ($proxy_host != '' && $proxy_user != '') {
  317.             $request .= 'Proxy-Authorization: Basic ' .
  318.                 base64_encode($proxy_user . ':' . $proxy_pass) . "\r\n";
  319.         }
  320.         if ($accept) {
  321.             $request .= 'Accept: ' . implode(', ', $accept) . "\r\n";
  322.         }
  323.         $request .= "Connection: close\r\n";
  324.         $request .= "\r\n";
  325.         if ($proxy_host != '') {
  326.             $fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr, 15);
  327.             if (!$fp) {
  328.                 return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr",
  329.                     -9276);
  330.             }
  331.         } else {
  332.             if (isset($info['scheme']) && $info['scheme'] == 'https') {
  333.                 $host = 'ssl://' . $host;
  334.             }
  335.             $fp = @fsockopen($host, $port, $errno, $errstr);
  336.             if (!$fp) {
  337.                 return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno);
  338.             }
  339.         }
  340.         fwrite($fp, $request);
  341.         $headers = array();
  342.         while (trim($line = fgets($fp, 1024))) {
  343.             if (preg_match('/^([^:]+):\s+(.*)\s*$/', $line, $matches)) {
  344.                 $headers[strtolower($matches[1])] = trim($matches[2]);
  345.             } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) {
  346.                 if ($matches[1] == 304 && ($lastmodified || ($lastmodified === false))) {
  347.                     return false;
  348.                 }
  349.                 if ($matches[1] != 200) {
  350.                     return PEAR::raiseError("File http://$host:$port$path not valid (received: $line)");
  351.                 }
  352.             }
  353.         }
  354.         if (isset($headers['content-length'])) {
  355.             $length = $headers['content-length'];
  356.         } else {
  357.             $length = -1;
  358.         }
  359.         $data = '';
  360.         while ($chunk = @fread($fp, 8192)) {
  361.             $data .= $chunk;
  362.         }
  363.         fclose($fp);
  364.         if ($lastmodified === false || $lastmodified) {
  365.             if (isset($headers['etag'])) {
  366.                 $lastmodified = array('ETag' => $headers['etag']);
  367.             }
  368.             if (isset($headers['last-modified'])) {
  369.                 if (is_array($lastmodified)) {
  370.                     $lastmodified['Last-Modified'] = $headers['last-modified'];
  371.                 } else {
  372.                     $lastmodified = $headers['last-modified'];
  373.                 }
  374.             }
  375.             return array($data, $lastmodified, $headers);
  376.         }
  377.         return $data;
  378.     }
  379. }
  380. ?>